<?php
/**
 * An Elgg 1.x compatible store implementation 
 */
 
 /**
 * Require base class for creating a new interface.
 */
 
require_once 'Auth/OpenID.php';
require_once 'Auth/OpenID/Interface.php';
require_once 'Auth/OpenID/Consumer.php';
require_once 'Auth/OpenID/HMACSHA1.php';
require_once 'Auth/OpenID/Nonce.php';
require_once 'Auth/OpenID/SReg.php';

class OpenID_ElggStore extends Auth_OpenID_OpenIDStore {

    function resetAssociations () {
        openid_client_delete_entities('object', 'openid_client::association');
    }
    function resetNonces () {
        openid_client_delete_entities('object', 'openid_client::nonce');
    }
    function getAssociation ($server_url, $handle = null) {
        if (isset($handle)) {
            $meta_array = array(
                        'server_url'    => $server_url,
                        'handle'        => $handle
            );
            $assocs = get_entities_from_metadata_multi($meta_array, 'object', 'openid_client::association');
        } else {
            $assocs = get_entities_from_metadata('server_url', $server_url, 'object','openid_client::association');
        }
        
        if (!$assocs || (count($assocs) == 0)) {
            return null;
        } else {
            $associations = array();

            foreach ($assocs as $assoc_row) {
                $assoc = new Auth_OpenID_Association($assoc_row->handle,
                                                     base64_decode($assoc_row->secret),
                                                     $assoc_row->issued,
                                                     $assoc_row->lifetime,
                                                     $assoc_row->assoc_type);

                if ($assoc->getExpiresIn() == 0) {
                    OpenID_ElggStore::removeAssociation($server_url, $assoc->handle);
                } else {
                    $associations[] = array($assoc->issued, $assoc);
                }
            }

            if ($associations) {
                $issued = array();
                $assocs = array();
                foreach ($associations as $key => $assoc) {
                    $issued[$key] = $assoc[0];
                    $assocs[$key] = $assoc[1];
                }

                array_multisort($issued, SORT_DESC, $assocs, SORT_DESC,
                                $associations);

                // return the most recently issued one.
                list($issued, $assoc) = $associations[0];
                return $assoc;
            } else {
                return null;
            }
        }
    }
    
    function removeAssociation ($server_url, $handle) {
        if (isset($handle)) {
            $meta_array = array(
                        'server_url'    => $server_url,
                        'handle'        => $handle
            );
            $entities = get_entities_from_metadata_multi($meta_array, 'object', 'openid_client::association');
        } else {
            $entities = get_entities_from_metadata('server_url', $server_url, 'object','openid_client::association');
        }
        foreach ($entities as $entity) {
			openid_client_delete_entity($entity);
		}
	}
    function reset () {
        OpenID_ElggStore::resetAssociations ();
        OpenID_ElggStore::resetNonces ();
    }
        
    function storeAssociation ($server_url, $association) {
        
        // Initialise a new ElggObject
		$association_obj = new ElggObject();
		
		$association_obj->subtype = 'openid_client::association';
		$association_obj->owner_guid = 0;
		$association_obj->container_guid = 0;
		$association_obj->title = 'association';
		$association_obj->access_id = 2;		
		
		if ($association_obj->save()) {		
    		$association_obj->server_url = $server_url;
    		$association_obj->handle = $association->handle;
            $association_obj->secret = base64_encode($association->secret);
            $association_obj->issued = $association->issued;
            $association_obj->lifetime = $association->lifetime;
            $association_obj->assoc_type = $association->assoc_type;
    		return true;
		} else {
    		return false;
		}
	}
		
    function useNonce ( $server_url,  $timestamp,  $salt) {
        global $Auth_OpenID_SKEW;

        if ( abs($timestamp - time()) > $Auth_OpenID_SKEW ) {
            return false;
        }
        
        // check to see if the nonce already exists
        
        $meta_array = array(
                        'server_url'    => $server_url,
                        'timestamp'     => $timestamp,
                        'salt'          => $salt
        );
        
        $entities = get_entities_from_metadata_multi($meta_array, 'object', 'openid_client::nonce');
        
        if ($entities) {
            // bad - this nonce is already in use
            return false;
        } else {        
            // Initialise a new ElggObject
    		$nonce_obj = new ElggObject();
    		
    		$nonce_obj->subtype = 'openid_client::nonce';
    		$nonce_obj->owner_guid = 0;
    		$nonce_obj->container_guid = 0;
    		$nonce_obj->title = 'nonce';
    		$nonce_obj->access_id = 2;
    		
    		if ($nonce_obj->save()) {
        		$nonce_obj->server_url = $server_url;
        		$nonce_obj->timestamp = $timestamp;
        		$nonce_obj->salt = $salt;
        		return true;
    		} else {
        		return false;
    		}
		}
	}
	
	function getNoSyncStatus($user) {
    	if (isset($user) && isset($user->openid_client_nosync_status)) {
        	return $user->openid_client_nosync_status;
    	} else {
        	return false;
    	}
	}
	
	function addNoSyncStatus($user) {
    	$user->openid_client_nosync_status = 1;
	}    	
}

function openid_client_create_invitation($prefix,$username,$ident,$email,$fullname) {
    
    $invite = new ElggObject();
		
	$invite->subtype = 'invitation';
	$invite->owner_guid = 0;
	$invite->container_guid = 0;
	$invite->title = 'invitation';
	$invite->access_id = 2;
	if ($invite->save()) {
    	$invite->new_owner = $ident;
    	$invite->name = $fullname;
    	$invite->email = $email;
    	$invite->username = $username;	
    	$invite->code = $prefix . substr(base_convert(md5(time() . $username), 16, 36), 0, 7);
    	$invite->added = time();
    	return $invite;
	} else {
    	return null;
	}
}

function openid_client_get_invitation($code) {
    $invitations = get_entities_from_metadata('code', $code, 'object','invitation');
    if ($invitations) {
        return $invitations[0];
    } else {
        return null;
    }    
}

function openid_client_remove_invitation($code) {
    $invitations = get_entities_from_metadata('code', $code, 'object','invitation');
    if ($invitations) {
        foreach ($invitations as $invitation) {
			openid_client_delete_entity($invitation);
		}
    }    
}

function openid_client_get_invitation_by_username($username) {
    $invitations = get_entities_from_metadata('username', $username, 'object','invitation');
    if ($invitations) {
        return $invitations[0];
    } else {
        return null;
    }    
}

function openid_client_send_activate_confirmation_message($details) {
    
	global $CONFIG;
	
	// not sure where these should really come from
	$from_name = $CONFIG->site->name;
	$from_email = $CONFIG->site->email;
	
	$subject = sprintf(elgg_echo('openid_client:activate_confirmation_subject'),$CONFIG->sitename);
	$url = $CONFIG->wwwroot . "mod/openid_client/actions/confirm.php?code=" . $details->code;

	$message = wordwrap(sprintf(elgg_echo('openid_client:activate_confirmation_body'),$details->name,$CONFIG->sitename,$url, $CONFIG->sitename));
	openid_client_email_user($details->name, $details->email, $from_name, $from_email, $subject,$message);
}

function openid_client_send_change_confirmation_message($details) {
	global $CONFIG;
	
	// not sure where these should really come from
	$from_name = 'System administrator';
	$from_email = 'kevin@radagast.biz';
	
	$subject = sprintf(elgg_echo('openid_client:change_confirmation_subject'),$CONFIG->sitename);
	$url = $CONFIG->wwwroot . "mod/openid_client/actions/confirm.php?code=" . $details->code;
	$message = wordwrap(sprintf(elgg_echo('openid_client:change_confirmation_body'),
	    $details->name,$CONFIG->sitename,$url, $CONFIG->sitename));
	openid_client_email_user($details->name, $details->email, $from_name, $from_email, $subject,$message);
}

$emailLabel = elgg_echo('openid_client:email_label');
$nameLabel = elgg_echo('openid_client:name_label');
$submitLabel = elgg_echo('openid_client:submit_label');
$cancelLabel = elgg_echo('openid_client:cancel_label');

function openid_client_generate_sync_form($new_email,$new_name, $user, $email_confirmation) {
	
	return elgg_view_layout('one_column',elgg_view_title(elgg_echo('openid_client:sync_title')) . elgg_view("openid_client/forms/sync", 
	    array(
	        'userid'                => $user->getGUID(),
	        'new_email'             => $new_email,
	        'new_name'              => $new_name,
	        'email_confirmation'    => $email_confirmation
        )));	
}


function openid_client_generate_missing_data_form($openid_url,$email,$fullname,$email_confirmation,$details) {

	return elgg_view_layout('one_column',elgg_view_title(elgg_echo('openid_client:missing_title')) . elgg_view("openid_client/forms/missing", 
	    array(
	        'openid_url'            => $openid_url,
	        'email'                 => $email,
	        'fullname'              => $fullname,
	        'email_confirmation'    => $email_confirmation,
	        'openid_code'                 => $details->code
        )));
}

function openid_client_check_email_confirmation($openid_url) {
	global $CONFIG;
	
	$done = false;	
	$email_confirmation = false;
	$greenlist = datalist_get('openid_client_greenlist');
	$yellowlist = datalist_get('openid_client_yellowlist');
	
	if ($greenlist) {		
		foreach (explode("\n",$greenlist) as $entry ) {
			if (fnmatch($entry,$openid_url)) {
				$email_confirmation = false;
				$done = true;
				break;
			}
		}
	}
	if (!$done && $yellowlist) {		
		foreach (explode("\n",$yellowlist) as $entry ) {
			if (fnmatch($entry,$openid_url)) {
				$email_confirmation = true;
				break;
			}
		}
	}
	return $email_confirmation;
}

function openid_client_create_openid_user($openid_url,$email, $fullname, $email_confirmation) {
	
	global $messages;
	
	if ($email && openid_client_get_user_by_email($email)) {
		register_error(sprintf(elgg_echo('openid_client:create_email_in_use'),$email));
		return null;
	} else {
					    
	    $user = new ElggUser();
		$user->email = $email;
		$user->name = $fullname;
		$user->access_id = 2;
		$user->subtype = 'openid';

		$user->username = randomString(8);
		
		if ($user->save()) {
    				
    		$id = $user->getGUID();
    		
    		$user = get_user($id);
    			
    		$user->alias = $openid_url;
    
    		$user->username = "openid_".$id;
    		
    		if ($email_confirmation) {
    			$user->active = 'no';
    		} else {
    			$user->active = 'yes';
    		}
    		
    		$r = $user->save();
		
		    return $user;
	    } else {
    	    register_error(elgg_echo('openid_client:user_creation_failed'));
    	    forward();
    	    return null;
	    }
	}						
} 

/**
 * Send a notification via email.
 */
function openid_client_email_user($to_name, $to_email, $from_name, $from_email, $subject, $message)
{	
    $to = "$to_name <$to_email>";
	
	$headers = "From: $from_name <$from_email>\r\n";
			
	return mail($to, $subject, $message, $headers);
}   

// should really be added to users.php

/**
 * Get user by email
 *
 * @param string $email The user's email address
 * @return ElggUser|false Depending on success
 */
function openid_client_get_user_by_email($email)
{
	global $CONFIG;
	
	$email = sanitise_string($email);
	$row = get_data_row("SELECT * from {$CONFIG->dbprefix}users_entity where email='$email'");
	if ($row)
		return new ElggUser($row); 
	
	return false;
}

// modified from Elgg 1.0 sessions.php

/**
* Log in
*
* @param user entity $user
* @param true|false $persistent
* @return true|false
*/
function do_login($user, $persistent = false) {
             
    $_SESSION['user'] = $user;
    $_SESSION['guid'] = $user->getGUID();
    $_SESSION['id'] = $_SESSION['guid'];
    $_SESSION['username'] = $user->username;
    $_SESSION['name'] = $user->name;
    
    $code = (md5($user->name . $user->username . time() . rand()));
    $user->code = md5($code);
    $user->save();
    
    $_SESSION['code'] = $code;
    //if (!empty($persistent)) {
    
    setcookie("elggperm", $code, (time()+(86400 * 30)),"/");
    
    //}
    // set_login_fields($user->id);

    return true; 
   
}

// copied over from old elgglib

/**
 * Validates an email to make sure it makes sense and adheres
 * to the email filter if it's set.
 *
 * @param string $address The email address to validate.
 * @return boolean
 */
function openid_validate_email($address) {

    global $CONFIG;
    
    if (ereg('^[-!#$%&\'*+\\./0-9=?A-Z^_`a-z{|}~]+'.
                  '@'.
                  '[-!#$%&\'*+\\/0-9=?A-Z^_`a-z{|}~]+\.'.
                  '[-!#$%&\'*+\\./0-9=?A-Z^_`a-z{|}~]+$',
                  $address)) {
                      
                      if ($CONFIG->emailfilter != "") {
                          $domain = substr($address,strpos($address,"@")+1);
                          if (substr_count($CONFIG->emailfilter, $domain) == 0) {
                              return false;
                          }
                      }
                      
                      return true;
                      
                  } else {
                      return false;
                  }
}


function randomString($length)
{
    // Generate random 32 charecter string
    $string = md5(time());

    // Position Limiting
    $highest_startpoint = 32-$length;

    // Take a random starting point in the randomly
    // Generated String, not going any higher then $highest_startpoint
    $randomString = substr($string,rand(0,$highest_startpoint),$length);

    return $randomString;

}

function openid_client_delete_entities($type = "", $subtype = "", $owner_guid = 0)
	{
		$entities = get_entities($type, $subtype, $owner_guid, "time_created desc", 0);
		
		foreach ($entities as $entity) {
			openid_client_delete_entity($entity);
		}
		
		return true;
	}
	
function openid_client_delete_entity($entity)
{
    global $CONFIG;
    	
    $entity->clearMetadata();
    $entity->clearAnnotations();
    $guid = $entity->getGUID();
	delete_data("DELETE from {$CONFIG->dbprefix}entities where guid={$guid}");
}

function is_admin($user_id = 0) {
    if (!$user_id) {
        if (isloggedin()) {
            $user_id = $_SESSION['user']->getGUID();
        } else {
            return false;
        }
    }
    
    return get_metadata_byname($user_id, 'admin');    
}

if (!function_exists('fnmatch')) {
function fnmatch($pattern, $string) {
   for ($op = 0, $npattern = '', $n = 0, $l = strlen($pattern); $n < $l; $n++) {
       switch ($c = $pattern[$n]) {
           case '\\':
               $npattern .= '\\' . @$pattern[++$n];
           break;
           case '.': case '+': case '^': case '$': case '(': case ')': case '{': case '}': case '=': case '!': case '<': case '>': case '|':
               $npattern .= '\\' . $c;
           break;
           case '?': case '*':
               $npattern .= '.' . $c;
           break;
           case '[': case ']': default:
               $npattern .= $c;
               if ($c == '[') {
                   $op++;
               } else if ($c == ']') {
                   if ($op == 0) return false;
                   $op--;
               }
           break;
       }
   }

   if ($op != 0) return false;

   return preg_match('/' . $npattern . '/i', $string);
}
}

?>